Home | | Latest | About | Random
# Week 2 Wednesday.
Dear Class,
Today we discuss the `string` class (section 6), as well as the unsigned types `unsigned int` and `size_t` (sections 5.8 and 5.9). On Friday we discuss the input buffer (section 7).
**Reminders**
- HW1 is due this Thursday at 10pm. `#include` the header for every function you use (look them up on cppreference), and be careful with `int` vs `double` and with explicit casting via `static_cast(...)`. And as we have not technically learned about the control flows `if` and `else`, try to solve the problems avoiding using those. You will gain an even better understanding!
- If you are on the waitlist (just a couple of you), I will try to resolve it sometimes Friday.
- If you have final exam conflict with another class's exam, see Announcement in BruinLearn.
- This week finish reading sections 5, 6, 7.
Ok, you can do it!
-- B.
.
.
.
.
.
.
.
## The mystery continues.
```cpp
#include
#include
using namespace std;
int main(){
string s = "Hello"; // This is one way to create a string
cout << s.length() << endl;
cout << s.length() - 10 << endl;
return 0;
}
```
How curious, 5 minus 10 is....??!
.
.
.
.
.
.
## `std::string` class. Storing text.
- So far our variables hold numbers, `bool`s, and single `char`s. To store **text** we use the `std::string` class.
- A **class** is a souped-up data type. Its superpower is **member functions** (or "methods") that can *do* things. (Later we will learn how to create [[1-teaching/ucla-2026-fall-pic-10a/class-sneak-peak|our own data type]])
- `#include `, even if your IDE lets you skip it.
Four ways to construct a `string`
```cpp
#include
#include
using namespace std;
int main(){
// different ways to create a string
string s1 = "Hello";
string s2("Can you hear me?");
string s3(10, '!');
string s0;
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
cout << s0 << endl;
// We can also use cin to take user input as a string.
// But there are some nuances that we will discuss next time.
// string name;
// cout << "What is your name? ";
// cin >> name;
// cout << "Aha, your name is " << name;
return 0;
}
```
- `s1`, `s2`, `s3`, `s0` are **instances** of the `string` class.
- Unlike an uninitialized `int`, the default-constructed `s0` is fine: it is the **empty string**.
- A string literal `"Hello"` is **not** a `string`.
**User input.** `cin >> name;` works with a `string` (try it, including typing a space; we explain the space on Friday).
- The `string` class data type has **member functions**, that can perform actions. Member functions are called with a **dot** `.` (*member access operator*):
```
[instance].[member function](arguments)
```
Try these on `s2`:
- `.length()`
- `.at(i)`
- `.substr(i)`
- `.substr(i, j)`
**Careful:** `substr(i, j)`: `j` is a **count** (at most `j` characters), not an end index.
- Functions like `pow`, `round`, `max`, `min` are **free functions** (no dot).
**READ Section 6.3**: what `substr` does at the edges (count too big is fine; position equal to length gives the empty string; position past the length is a runtime error).
.
.
.
.
.
.
## Indexing.
```
s = H e l l o
index = 0 1 2 3 4
```
- Counting starts at `0`: say **0-th**, **1-th**, **2-th**. `s.at(0)` and `s[0]` are both `'H'`.
- The last character is `s[s.length() - 1]`.
- **`at` vs `[ ]`:** `at` checks bounds and gives a **runtime error** if out of range. `[ ]` gives **undefined behavior**.
**Example.** (build error, runtime error, or undefined behavior?). With `string s = "abcdef";`
1. `cout << s[8];`
2. `cout << s.at(8);`
**Practice.**
Given a name in lower case `string my_name = "joe";`, capitalize the first letter. Try to do it in a way that accounts for any lower case name.
**BUT WAIT. What is the type of the number going into `at(i)`, and the type of the number coming out of `length()`?**
.
.
.
.
.
.
.
## `unsigned int` and `size_t`: never negative
- An `int` is about -2 billion to 2 billion.
- An `unsigned int` is about 0 to 4 billion (2^32 - 1).
- But what happens at the **edge**?
```cpp
#include
using namespace std;
int main() {
unsigned int x = 0;
cout << x << endl;
x--;
cout << x << endl; // ???
unsigned int y = -1;
cout << y << endl; // ???
y++;
cout << y << endl; // ???
int i = -1;
unsigned int u = 0;
cout << i + u << endl; // ???
return 0;
}
```
- This **wraparound** is *defined* for `unsigned int` (works modulo 2^32) but *undefined* for `int`.
- **Mixed arithmetic:** when an `int` meets an `unsigned int`, the `int` is converted to unsigned first. So `-1 + 0u` is `4294967295`.
- **READ 5.8.2 and 5.8.3**, starting with **Definition 5.8.2**, on casting between `int` and `unsigned int`.
- Rule of thumb: unsigned types exist because **sizes and indices** are unsigned. For ordinary quantities (money, temperatures, differences), prefer `int` or `double`.
### `size_t`
- `size_t` (from ``) is an unsigned type big enough to describe the size of any object on the computer. On 64-bit builds it is 0 to about $18 \times 10^{18}$ (18 quintillion). Some machines are 32-bits, beware!
- It is the right type to store **lengths** and **indices**. It wraps around like any unsigned type.
```cpp
size_t len = s.length();
size_t index = 0;
```
Can you now figure out our original mystery?
- `s.length()` is a `size_t`, so `s.length() - 10` is `5 - 10` computed as an **unsigned** number. It wraps around to `18446744073709551611`. The same happens with the empty string: `e.length() - 1` is `18446744073709551615`, not `-1`.
- Remember `18446744073709551615`. It is `static_cast(-1)`. Sometimes this is used to indicate something is not found.
.
.
.
.
.
.
.
## Member functions and operations with `string`.
- Besides using computers to do numerical calculations, another common endeavor is parsing and manipulating text (reading and cleaning up data, extracting information, scraping websites, etc.)
- Let `s`, `t` be `string`s:
| Expression | What it gives |
| ----------------- | ------------------------------------------------------- |
| `s.at(i)`, `s[i]` | the `i`-th character |
| `s.length()` | number of characters (a `size_t`) |
| `s.substr(i)` | substring from index `i` to the end |
| `s.substr(i, j)` | substring from `i` with at most `j` characters |
| `s.find(t)` | index of the **first** occurrence of `t` in `s` |
| `s.find(t, i)` | first occurrence at index `i` or later |
| `s.rfind(t)` | index of the **last** occurrence of `t` in `s` |
| `s.rfind(t, i)` | last occurrence starting at or before index `i` |
| `s + t` | a new `string`: `s` followed by `t` (**concatenation**) |
| `stod(s)` | converts `s` to a `double` |
**1. `find` and `rfind` return a `size_t`.** Store them in a `size_t`, not an `int`.
```cpp
#include
#include
using namespace std;
int main() {
string fruit = "banana";
cout << fruit.find("na") << endl; // 2
cout << fruit.rfind("na") << endl; // 4
cout << fruit.find("ma") << endl;
// 18446744073709551615 ... remember our friend?
return 0;
}
```
- When nothing is found you get `string::npos`, which is `static_cast(-1)`. That is the wraparound number from the mystery earlier. Test with `== string::npos`.
```cpp
#include
#include
using namespace std;
int main() {
string x = "birdcowfish";
size_t p;
p = x.rfind("cow"); cout << p << endl;
p = x.rfind("cow", 4); cout << p << endl;
p = x.rfind("cow", 3); cout << p << endl;
return 0;
}
```
**2. `+` needs a `string` on at least one side.**
```cpp
#include
#include
using namespace std;
int main() {
string a = "Hello";
string b = a + ", world!"; cout << b; // OK
// string c = "Hello" + ", world!";
// BUILD ERROR: two string literals
return 0;
}
```
**3. `stod` reads the longest numeric prefix and ignores the rest.**
```cpp
cout << stod("0.125") * 4.5 << endl; // 0.5625
cout << stod("-3.5abc") << endl; // -3.5
cout << stod("+0.1875=7") << endl; // 0.1875
// stod("abc") is a runtime error
```
**4. Avoid magic numbers.** Try not to hard code special lengths and positions with literal numerical values like `4`, but instead compute them by `.length()` or `.find(t,j)`. And compute them out of `const string` to avoid manual counting. (See DNA example below.)
**READ. Find on cppreference `+=`, `push_back`, `pop_back` for `string`.**
.
.
.
.
.
----
**Postscript.**
**We ended here. Try the following DNA analysis example at home!**
## Example. DNA analysis.
Suppose you are given this piece of DNA sequence (in letters `G, A, T, C`):
```cmd
AATCGCCACCGGAAGCAAGAGGGAGCTGGTCTGGATATCGTGATCCTTTGGGTAAAGATTGGAGTTAACACCTTCGGTGTCCAGCAATGCTAATACGGTTGGGAAACATATAGGACTCGCGGCAGCTGGTGGATCGTGACAAATAAAGGGAAATCCCGTTCTCGGGTCAAGTAGTTCTCTCATGAACCAAGCCGACCTCAGAGGTACGGATGTAAAGGTTACCAGGGCTATCGGAACTAGATCGGTACAGGACGTGGACAGTATGCAACCATAATGCGTCATTAATGGGTTTATGAGCCT
```
**Start by:** Initialize a const string `const string dna = "AATC...";` (Read the note on `const` below)
**Tasks. (Try at home if we didn't finish in class)**
1. Every block of 5 letters is a *section*. Print the first and the sixth section (English sense!).
2. `GGGAAA` is cancerous. Hint: initialize a const string `const string cancerous = "GGGAAA";`
- Find the 4 letters right after the first occurrence of this cancerous sequence.
- Is there a second occurrence? A third? (What does `find` give you?)
- What if instead `CAAATAAA` is cancerous? Repeat the previous two subparts.
1. Make a new DNA sequence with the first occurrence of `CAAATAAA` removed.
2. The sub-DNA strictly between the first `GGGAAA` and the last `CATAA`.
3. From `"temp=96F;hum=53%"`, get two `double`s and print their sum.
[[1-teaching/ucla-2026-fall-pic-10a/DNA-answers|Suggested answer to above.]]
## A note on `const`.
Recall `const` on a variable "locks it" and make it read only.
Here both `dna = "...."` and `cancerous = "GGGAAA"` are fixed pieces of information. They should not get changed during the program. And by putting `const` we can enforce that.
```cpp
#include
#include
using namespace std;
int main() {
const string dna = "CCCCCGGGAAAACCCCCCC";
const string cancerous = "GGGAAA";
// cancerous = "AAA"; // BUILD ERROR: can't assign to a const
// cancerous[0] = 'T'; // BUILD ERROR: can't modify its characters
cout << cancerous.length() << endl; // fine: just looking
cout << dna.find(cancerous) << endl; // fine: just looking
cout << dna.substr(3); // fine: we are making a new string
return 0;
}
```
.